package com.zm.boss.service.service;

import com.zm.boss.service.common.enums.ErrorCodeEnum;
import com.zm.boss.service.common.enums.SummaryEnum;
import com.zm.boss.service.exception.BizException;
import com.zm.boss.service.repository.mapper.BaseMapper;
import com.zm.boss.service.request.PageRequest;
import com.zm.boss.service.response.Attr;
import com.zm.boss.service.response.PaginationRespTemplate;
import com.zm.boss.service.response.Summary;
import com.zm.boss.service.util.AESUtil;
import com.zm.boss.service.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.Charsets;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toMap;

@Slf4j
public abstract class BaseServiceUpImpl<T,R> implements BaseServiceUp<T,R>{
    public abstract BaseMapper getMapper();

    @Override
    public Integer save(T params, ErrorCodeEnum errorCodeEnum, String... isExitsKey) {
        Map<String, Object> paramsMap = initParams(params);
        if(isExitsKey != null && isExitsKey.length != 0){
            Map<String, Object> queryMap = new HashMap<>();
            Stream.of(isExitsKey).forEach(d -> {
                Optional<Map.Entry<String, Object>> op = paramsMap.entrySet().stream().filter(e -> StringUtils.equals(e.getKey(), d)).findFirst();
                if(op.isPresent()){
                    Map.Entry<String, Object> map = op.get();
                    queryMap.put(map.getKey(), map.getValue());
                }
            });
            T t = (T) getMapper().selectOneByMap(queryMap);
            if(t != null){
                throw new BizException(errorCodeEnum.getErrorCode(),errorCodeEnum.getErrorMsg());
            }
        }
        return getMapper().insertSelectiveByMap(paramsMap);
    }
    @Override
    public Integer save(T params, ErrorCodeEnum errorCodeEnum, String key, List<String> encryptionKey, String... isExitsKey) {
        Map<String, Object> paramsMap = initParams(params);
        paramsMap.entrySet().stream().forEach(d -> {
            encryptionKey.stream().filter(l -> StringUtils.equals(l, d.getKey()))
                    .forEach(e -> {
                        try {
                            d.setValue(AESUtil.encrypt(d.getValue() + "",key, Charsets.UTF_8.toString()));
                        } catch (Exception ex) {
                            log.error("加密异常");
                        }
                    });
        });
        if(isExitsKey != null && isExitsKey.length != 0){
            Map<String, Object> queryMap = new HashMap<>();
            Stream.of(isExitsKey).forEach(d -> {
                Optional<Map.Entry<String, Object>> op = paramsMap.entrySet().stream().filter(e -> StringUtils.equals(e.getKey(), d)).findFirst();
                if(op.isPresent()){
                    Map.Entry<String, Object> map = op.get();
                    queryMap.put(map.getKey(), map.getValue());
                }
            });
            T t = (T) getMapper().selectOneByMap(queryMap);
            if(t != null){
                throw new BizException(errorCodeEnum.getErrorCode(),errorCodeEnum.getErrorMsg());
            }
        }

        return getMapper().insertSelectiveByMap(paramsMap);
    }
    @Override
    public List<R> query(T params) {
        Map<String, Object> paramsMap = null;
        if(params instanceof String){
            String json = new String(URLDecoder.decode(new String(Base64.decodeBase64((String) params))));
            PageRequest pageRequest = JsonUtil.toObject((String) json, PageRequest.class);
            paramsMap = JsonUtil.toObject((String) json, Map.class);
            Map<String, Object> pageMap = JsonUtil.toObject(JsonUtil.toJson(pageRequest), Map.class);
            paramsMap.putAll(pageMap);
        }else if(params instanceof Map){
            paramsMap = (Map<String, Object>) params;
        }else{
            paramsMap = JsonUtil.toObject(JsonUtil.toJson(params), Map.class);
        }
        log.info("入参： [{}]", paramsMap.toString());
        return getMapper().selectByMap(paramsMap);
    }

    @Override
    public Integer update(T params) {
        Map<String, Object> paramsMap = initParams(params);
        log.info("入参： [{}]", paramsMap.toString());
        return getMapper().updateByMapSelective(paramsMap);
    }

    @Override
    public Integer save(T params) {
        Map<String, Object> paramsMap = initParams(params);
        log.info("入参： [{}]", paramsMap.toString());
        if(paramsMap.get("id") != null){
            return getMapper().updateByMapSelective(paramsMap);
        }

        return getMapper().insertSelectiveByMap(paramsMap);
    }

    @Override
    public Integer delete(T params) {
        Map<String, Object> paramsMap = initParams(params);
        log.info("入参： [{}]", paramsMap.toString());
        return getMapper().deleteByMap(paramsMap);
    }

    @Override
    public Integer count(T params) {
        Map<String, Object> paramsMap = initParams(params);
        log.info("入参： [{}]", paramsMap.toString());
        return getMapper().selectCountByMap(paramsMap);
    }

    @Override
    public R findById(T params) {
        Map<String, Object> paramsMap = initParams(params);
        log.info("入参： [{}]", paramsMap.toString());
        return (R) getMapper().selectOneByMap(paramsMap);
    }

    @Override
    public Map<String, Object> initParams(T params) {
        Map<String, Object> paramsMap = null;
        if (params instanceof String) {
            String json = new String(URLDecoder.decode(new String(Base64.decodeBase64((String) params))));
            paramsMap = JsonUtil.toObject((String) json, Map.class);
        } else if (params instanceof Map) {
            paramsMap = (Map<String, Object>) params;
        } else {
            paramsMap = JsonUtil.toObject(JsonUtil.toJson(params), Map.class);
        }
        return paramsMap;
    }

    @Override
    public PaginationRespTemplate<R> queryPage(T params, PaginationRespTemplate<R> paginationRespTemplate, String title, Supplier<Object[]> supplier, Function<Object, List<Attr>> attr) {
        paginationRespTemplate.setContent(this.query(params));
        Integer totalCount = this.count(params);
        Summary summary = new Summary();
        summary.setTitle(title);
        if(supplier != null){
            paginationRespTemplate.setHeader(supplier.get());
        }
        if(attr != null){
            List<Attr> attrs = attr.apply(initParams(params));
            attrs.forEach(d ->  summary.addAttr(d.getSummaryEnum().getKey(), d));
        }
        Attr attrCount = Attr.builder().label(SummaryEnum.TOTAL_COUNT.getValue()).value(Optional.ofNullable(totalCount).isPresent() ? totalCount : 0).build();
        summary.addAttr(SummaryEnum.TOTAL_COUNT.getKey(), attrCount);
        paginationRespTemplate.setSummary(summary);
        return paginationRespTemplate;
    }

}
